load libraries

library(survival)
library(survminer)
library(cgdsr)
library(sparklyr)
library(dplyr)

Working with R Session

Load clinical Data

cgds <- cgdsr::CGDS("http://www.cbioportal.org/public-portal/")
#Studies<- cgdsr::getCancerStudies(cgds)
clinicalData <- cgdsr::getClinicalData(cgds, "gbm_tcga_pub_all")
#clinicalData <- read.csv("Clinical_tab.csv") #, na.strings=c("","NA")
clinicalData[c('DFS_MONTHS','DFS_STATUS', 'OS_MONTHS', 'OS_STATUS', 'TREATMENT_STATUS' )]

Transformations 1

clinicalData$OS_STATUS <- gsub("LIVING", "0", clinicalData$OS_STATUS, ignore.case = TRUE)
clinicalData$OS_STATUS <- gsub("DECEASED", "1", clinicalData$OS_STATUS, ignore.case = TRUE)
clinicalData$DFS_STATUS <- gsub("^$|^ $", "DiseaseFree", clinicalData$DFS_STATUS, ignore.case = TRUE)
clinicalData$OS_STATUS <- as.numeric(clinicalData$OS_STATUS)

survival plot

fit <- survival::survfit(Surv(OS_MONTHS, OS_STATUS) ~ DFS_STATUS, data = clinicalData)
   survminer::ggsurvplot(fit, data = clinicalData,
                          type = "kaplan-meier",
                          #conf.type="log",
                          conf.int = TRUE,
                          pval = TRUE,
                          fun = "pct",
                          risk.table = TRUE,
                          size = 1,
                          linetype = "strata",
                          palette = c("#E7B800", "#2E9FDF"),
                          legend = "top",
                          lengend.title = "DFS_STATUS",
                          legend.labs = c("DiseaseFree", "Recurred")
   )

R session VS Spark

Plot DiseaseFree vs Reccured during OS_MONTHS

  clinicalData <- cgdsr::getClinicalData(cgds, "gbm_tcga_pub_all")
  start_time <- Sys.time()
  clinicalData %>% 
  mutate(OS_STATUS = gsub("LIVING", "0", OS_STATUS)) %>%
  mutate(OS_STATUS = gsub( "DECEASED", "1", OS_STATUS)) %>%
  mutate(DFS_STATUS = gsub( "^$|^ $", "DiseaseFree", DFS_STATUS)) %>%
  mutate(OS_STATUS = as.numeric(OS_STATUS)) %>%
  arrange(OS_MONTHS) %>%
  mutate( DiseaseFree = ifelse(DFS_STATUS == "DiseaseFree", 1, 0)) %>% 
  as.data.frame() %>%
  mutate(n_DiseaseFree = cumsum(DiseaseFree == 1)) %>%
  mutate(n_Recurred = cumsum(DiseaseFree == 0)) %>%
  ggplot(aes(x = OS_MONTHS, y = value, color = variable)) +
  geom_point(aes(y = n_DiseaseFree, col = "n_DiseaseFree")) +
  geom_point(aes(y = n_Recurred, col = "n_Recurred")) +
  labs(title = paste("Using R Session, Running time = ", Sys.time() - start_time))

Spark Node: Plot DiseaseFree vs Reccured during OS_MONTHS

 clinicalData <- cgdsr::getClinicalData(cgds, "gbm_tcga_pub_all")
 sc <- spark_connect(master = "local",
                     version = "2.4.0")
Re-using existing Spark connection to local
 clinicalData_tbl <- dplyr::copy_to(sc, clinicalData, overwrite = TRUE)
  start_time <- Sys.time()
  clinicalData_tbl %>%
  mutate(OS_STATUS = regexp_replace(OS_STATUS, "LIVING", "0")) %>%
  mutate(OS_STATUS = regexp_replace(OS_STATUS, "DECEASED", "1")) %>%
  mutate(DFS_STATUS = regexp_replace(DFS_STATUS, "^$|^ $", "DiseaseFree")) %>%
  mutate(OS_STATUS = as.numeric(OS_STATUS)) %>%
  #mutate(OS_STATUS = regexp_replace(as.numeric(OS_STATUS), 'NaN', NA)) %>%
  #mutate(OS_STATUS = regexp_replace(OS_STATUS, NaN, NA)) %>%
  #na.replace('') %>%  ## not good for OS_STATUS (0,1)
  #dplyr::filter(!is.na(OS_MONTHS)) 
  arrange(is.na(OS_MONTHS), OS_MONTHS) %>%  ## OUFFF put Nan at the end of the column
  mutate(DiseaseFree = ifelse(DFS_STATUS == "DiseaseFree", 1, 0)) %>% 
  as.data.frame() %>%
  mutate( n_DiseaseFree = cumsum(as.numeric(DiseaseFree == 1 ))) %>%
  mutate( n_Recurred = cumsum(as.numeric(DiseaseFree == 0 ))) %>%
  ggplot(aes(x = OS_MONTHS, y = value, color = variable)) +
  geom_point(aes(y = n_DiseaseFree, col = "n_DiseaseFree")) +
  geom_point(aes(y = n_Recurred, col = "n_Recurred"))  +
   labs(title = paste("Using Spark Node, Running time = ", Sys.time() - start_time))

Survival regression using ml_aft_survival_regression

Ovarian Data from survival package

library(survival)
ovarian

Predict Survival regression using spark

# MAGIC - **futime**: survival or censoring time
# MAGIC - **fustat**: censoring status
# MAGIC - **age**:  in years
# MAGIC - **resid_ds**: residual disease present (1=no, 2=yes)
# MAGIC - **rx**:   treatment group
# MAGIC - **ecog.ps**:  ECOG performance status (1 is better, see reference)
sc <- spark_connect(master = "local",
                     version = "2.4.0")
Re-using existing Spark connection to local
ovarian_tbl <- sdf_copy_to(sc, ovarian, name = "ovarian_tbl", overwrite = TRUE)
#spark.survreg(ovarian_tbl, Surv(futime, fustat) ~ ecog_ps + rx)
partitions <- ovarian_tbl %>%
  sdf_partition(training = 0.7, test = 0.3, seed = 1111)
ovarian_training <- partitions$training
ovarian_test <- partitions$test
sur_reg <- ovarian_training %>%
  ml_aft_survival_regression(futime ~ ecog_ps + rx + age + resid_ds, censor_col = "fustat")
pred <- ml_predict(sur_reg, ovarian_test)
pred

Extract parameters

intercept <- sur_reg$coefficients[1]
coefficients <- sur_reg$coefficients[c(2,3)]
sur_reg$coefficients
(Intercept)     ecog_ps          rx         age    resid_ds 
11.69793764 -0.27694569  0.65752740 -0.07862066 -0.79872945 
plotParams <- round(ovarian[c('resid.ds', 'rx', 'ecog.ps', 'age')])
scale <- exp(intercept + as.matrix(plotParams) * coefficients)
cbind(plotParams, scale)
tSeq <- ovarian$futime # seq(0, 5E3, 50)
probs <- data.frame(t = tSeq)
for (i in 1:2^4) { 
  probs[, paste("(resid.ds, rx, ecog.ps, age) = (", toString(plotParams[i, ]), ")", sep = "")] <- 
    pweibull(tSeq, shape = 1, scale = scale[i], lower.tail = F)
}
probs

Melt the DataFrame

# MAGIC - **futime**: survival or censoring time
# MAGIC - **fustat**: censoring status
# MAGIC - **age**:  in years
# MAGIC - **resid_ds**: residual disease present (1=no, 2=yes)
# MAGIC - **rx**:   treatment group
# MAGIC - **ecog.ps**:  ECOG performance status (1 is better, see reference)
melted <- melt(probs, id.vars="t", variable.name="group", value.name="prob")
melted

Plot survival regression

library(ggplot2)
library(reshape2)
ggplot(data=melted, aes(x=t, y=prob, group=group, color=group)) + 
  geom_point() +
  #geom_smooth() +
  #geom_jitter() +
  labs(x = "time", y = "Survival probability")

Clinical Data from gbm_tcga_pub_all Study from cBioPortal

# > ovarian_training
# # Source: spark<?> [?? x 6]
# futime fustat   age resid_ds    rx ecog_ps
# *  <dbl>  <dbl> <dbl>    <dbl> <dbl>   <dbl>
# 1    156      1  66.5        2     1       2
# 2    329      1  43.1        2     1       1
# 3    353      1  63.2        1     2       2
# 4    365      1  64.4        2     2       1
# 5    377      0  58.3        1     2       1
# 6    421      0  53.4        2     2       1
# 7    448      0  56.4        1     1       2
# 8    464      1  56.9        2     2       2
# 9    475      1  59.9        2     2       2
# 10   563      1  55.2        1     2       2
# # ... with more rows
clinicalData <- cgdsr::getClinicalData(cgds, "gbm_tcga_pub_all")
#clinicalData <- read.csv("Clinical_tab.csv") #, na.strings=c("","NA")
clinicalData <- clinicalData[c('OS_MONTHS',  'OS_STATUS', 'DFS_STATUS',  'TREATMENT_STATUS' )]
sc <- spark_connect(master = "local",
                     version = "2.4.0")
Re-using existing Spark connection to local
 clinicalData_tbl <- dplyr::copy_to(sc, clinicalData, overwrite = TRUE)
  start_time <- Sys.time()
  clinicalData_trans_tbl <- 
  clinicalData_tbl %>%
  mutate(OS_STATUS = regexp_replace(OS_STATUS, "LIVING", 1)) %>%
  mutate(OS_STATUS = regexp_replace(OS_STATUS, "DECEASED", 0)) %>%
  mutate(DFS_STATUS = regexp_replace(DFS_STATUS, "^$|^ $", "DiseaseFree")) %>%
  mutate(DFS_STATUS = regexp_replace(DFS_STATUS, "DiseaseFree", 1)) %>%
  mutate(DFS_STATUS = regexp_replace(DFS_STATUS, "Recurred", 2)) %>%
  mutate(xr = ifelse(TREATMENT_STATUS == "Untreated", 1 , 2)) %>%
  mutate(xr = ifelse(TREATMENT_STATUS == "Treated", 2, 1)) %>%
  mutate(OS_STATUS = as.numeric(OS_STATUS)) %>%
  mutate(DFS_STATUS = as.numeric(DFS_STATUS)) %>%
  #arrange(is.na(OS_MONTHS), OS_MONTHS) %>% ## OUFFF put Nan at the end of the column
  na.replace(1)
  clinicalData_trans <- as.data.frame(clinicalData_trans_tbl)
  clinicalData_trans_tbl
partitions_clinicalData <- clinicalData_trans_tbl %>%
  sdf_partition(training = 0.7, test = 0.3, seed = 1111)
clinicalData_training <- partitions_clinicalData$training
clinicalData_test <- partitions_clinicalData$test
sur_reg_clinicalData <- clinicalData_training %>%
  ml_aft_survival_regression(OS_MONTHS ~ DFS_STATUS + xr, censor_col = "OS_STATUS")
pred_clinicalData <- ml_predict(sur_reg_clinicalData, clinicalData_test)
pred_clinicalData

Extract parameters for Clinical Data

intercept_clinicalData <- sur_reg_clinicalData$coefficients[1]
coefficients_clinicalData <- sur_reg$coefficients[c(2,3)]
sur_reg_clinicalData$coefficients
(Intercept)  DFS_STATUS          xr 
  -2.316624    5.763584   -1.634578 
plotParams_clinicalData <- clinicalData_trans[c('DFS_STATUS', 'xr')]
scale_clinicalData <- exp(intercept_clinicalData + as.matrix(plotParams_clinicalData) * coefficients_clinicalData)
cbind(plotParams_clinicalData, scale_clinicalData)
tSeq_clinicalData <- clinicalData_trans$OS_MONTHS # seq(0, 5E3, 50)
probs_clinicalData <- data.frame(t = tSeq_clinicalData)
for (i in 1:5) { 
  probs_clinicalData[, paste("(DFS_STATUS, xr) = (", toString(plotParams_clinicalData[i, ]), ")", sep = "")] <- 
    pweibull(tSeq_clinicalData, shape = 1, scale = scale_clinicalData[i], lower.tail = F)
}
probs_clinicalData
melted_clinicalData <- melt(probs_clinicalData, id.vars="t", variable.name="group", value.name="prob")
melted_clinicalData
library(ggplot2)
library(reshape2)
ggplot(data=melted_clinicalData, aes(x=t, y=prob, group=group, color=group)) + 
  geom_point() +
  #geom_smooth() +
  #geom_jitter() +
  labs(x = "time", y = "Survival probability")

LS0tCnRpdGxlOiAic3Vydml2YWwgcGxvdDogUiBTZXNzaW9uICB2cyBTcGFyayBOb2RlIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogICAgZmlnX2NhcHRpb246IHRydWUKICAgIHRvYzogdHJ1ZQogICAgZmlnX3dpZHRoOiA3CiAgICBmaWdfaGVpZ2h0OiA0LjUKICAgIHRoZW1lOiBjb3NtbwogICAgaGlnaGxpZ2h0OiB0YW5nbwogICAgI2NvZGVfZm9sZGluZzogaGlkZQogICAgaHRtbF9ub3RlYm9vazogbnVsbAogICAgZGZfcHJpbnQ6IHBhZ2VkICMjIFVzZSBybWFya2Rvd246OnBhZ2VkX3RhYmxlIHRvIGNyZWF0ZSBhIHBhZ2VhYmxlIHRhYmxlCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0UsIGVjaG89RkFMU0V9CiNrbml0cjo6b3B0c19jaHVuayRzZXQoZWNobz1UUlVFLCBlcnJvcj1GQUxTRSkKYGBgCgojIGxvYWQgbGlicmFyaWVzIHsudGFic2V0IC50YWJzZXQtZmFkZSAudGFic2V0LXBpbGxzfQpgYGB7ciBpbXBvcnQsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoc3Vydml2YWwpCmxpYnJhcnkoc3Vydm1pbmVyKQpsaWJyYXJ5KGNnZHNyKQpsaWJyYXJ5KHNwYXJrbHlyKQpsaWJyYXJ5KGRwbHlyKQpgYGAKCiMgIFdvcmtpbmcgd2l0aCBSIFNlc3Npb24gey50YWJzZXQgLnRhYnNldC1mYWRlIC50YWJzZXQtcGlsbHN9CgojIyBMb2FkIGNsaW5pY2FsIERhdGEKYGBge3J9CmNnZHMgPC0gY2dkc3I6OkNHRFMoImh0dHA6Ly93d3cuY2Jpb3BvcnRhbC5vcmcvcHVibGljLXBvcnRhbC8iKQojU3R1ZGllczwtIGNnZHNyOjpnZXRDYW5jZXJTdHVkaWVzKGNnZHMpCmNsaW5pY2FsRGF0YSA8LSBjZ2Rzcjo6Z2V0Q2xpbmljYWxEYXRhKGNnZHMsICJnYm1fdGNnYV9wdWJfYWxsIikKCiNjbGluaWNhbERhdGEgPC0gcmVhZC5jc3YoIkNsaW5pY2FsX3RhYi5jc3YiKSAjLCBuYS5zdHJpbmdzPWMoIiIsIk5BIikKCmNsaW5pY2FsRGF0YVtjKCdERlNfTU9OVEhTJywnREZTX1NUQVRVUycsICdPU19NT05USFMnLCAnT1NfU1RBVFVTJywgJ1RSRUFUTUVOVF9TVEFUVVMnICldCgpgYGAKCgojIyBUcmFuc2Zvcm1hdGlvbnMgMQoKYGBge3J9CmNsaW5pY2FsRGF0YSRPU19TVEFUVVMgPC0gZ3N1YigiTElWSU5HIiwgIjAiLCBjbGluaWNhbERhdGEkT1NfU1RBVFVTLCBpZ25vcmUuY2FzZSA9IFRSVUUpCmNsaW5pY2FsRGF0YSRPU19TVEFUVVMgPC0gZ3N1YigiREVDRUFTRUQiLCAiMSIsIGNsaW5pY2FsRGF0YSRPU19TVEFUVVMsIGlnbm9yZS5jYXNlID0gVFJVRSkKY2xpbmljYWxEYXRhJERGU19TVEFUVVMgPC0gZ3N1YigiXiR8XiAkIiwgIkRpc2Vhc2VGcmVlIiwgY2xpbmljYWxEYXRhJERGU19TVEFUVVMsIGlnbm9yZS5jYXNlID0gVFJVRSkKY2xpbmljYWxEYXRhJE9TX1NUQVRVUyA8LSBhcy5udW1lcmljKGNsaW5pY2FsRGF0YSRPU19TVEFUVVMpCgpgYGAKCiMjIHN1cnZpdmFsIHBsb3QKCmBgYHtyfQpmaXQgPC0gc3Vydml2YWw6OnN1cnZmaXQoU3VydihPU19NT05USFMsIE9TX1NUQVRVUykgfiBERlNfU1RBVFVTLCBkYXRhID0gY2xpbmljYWxEYXRhKQogICBzdXJ2bWluZXI6Omdnc3VydnBsb3QoZml0LCBkYXRhID0gY2xpbmljYWxEYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAia2FwbGFuLW1laWVyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAjY29uZi50eXBlPSJsb2ciLAogICAgICAgICAgICAgICAgICAgICAgICAgIGNvbmYuaW50ID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICBwdmFsID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICBmdW4gPSAicGN0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICByaXNrLnRhYmxlID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMSwKICAgICAgICAgICAgICAgICAgICAgICAgICBsaW5ldHlwZSA9ICJzdHJhdGEiLAogICAgICAgICAgICAgICAgICAgICAgICAgIHBhbGV0dGUgPSBjKCIjRTdCODAwIiwgIiMyRTlGREYiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQgPSAidG9wIiwKICAgICAgICAgICAgICAgICAgICAgICAgICBsZW5nZW5kLnRpdGxlID0gIkRGU19TVEFUVVMiLAogICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5sYWJzID0gYygiRGlzZWFzZUZyZWUiLCAiUmVjdXJyZWQiKQogICApCmBgYAoKIyBSIHNlc3Npb24gVlMgU3Bhcmsgey50YWJzZXQgLnRhYnNldC1mYWRlIC50YWJzZXQtcGlsbHN9CgojIyBQbG90IERpc2Vhc2VGcmVlIHZzIFJlY2N1cmVkIGR1cmluZyBPU19NT05USFMKYGBge3J9CiAgY2xpbmljYWxEYXRhIDwtIGNnZHNyOjpnZXRDbGluaWNhbERhdGEoY2dkcywgImdibV90Y2dhX3B1Yl9hbGwiKQogIHN0YXJ0X3RpbWUgPC0gU3lzLnRpbWUoKQogIGNsaW5pY2FsRGF0YSAlPiUgCiAgbXV0YXRlKE9TX1NUQVRVUyA9IGdzdWIoIkxJVklORyIsICIwIiwgT1NfU1RBVFVTKSkgJT4lCiAgbXV0YXRlKE9TX1NUQVRVUyA9IGdzdWIoICJERUNFQVNFRCIsICIxIiwgT1NfU1RBVFVTKSkgJT4lCiAgbXV0YXRlKERGU19TVEFUVVMgPSBnc3ViKCAiXiR8XiAkIiwgIkRpc2Vhc2VGcmVlIiwgREZTX1NUQVRVUykpICU+JQogIG11dGF0ZShPU19TVEFUVVMgPSBhcy5udW1lcmljKE9TX1NUQVRVUykpICU+JQogIGFycmFuZ2UoT1NfTU9OVEhTKSAlPiUKICBtdXRhdGUoIERpc2Vhc2VGcmVlID0gaWZlbHNlKERGU19TVEFUVVMgPT0gIkRpc2Vhc2VGcmVlIiwgMSwgMCkpICU+JSAKICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgbXV0YXRlKG5fRGlzZWFzZUZyZWUgPSBjdW1zdW0oRGlzZWFzZUZyZWUgPT0gMSkpICU+JQogIG11dGF0ZShuX1JlY3VycmVkID0gY3Vtc3VtKERpc2Vhc2VGcmVlID09IDApKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBPU19NT05USFMsIHkgPSB2YWx1ZSwgY29sb3IgPSB2YXJpYWJsZSkpICsKICBnZW9tX3BvaW50KGFlcyh5ID0gbl9EaXNlYXNlRnJlZSwgY29sID0gIm5fRGlzZWFzZUZyZWUiKSkgKwogIGdlb21fcG9pbnQoYWVzKHkgPSBuX1JlY3VycmVkLCBjb2wgPSAibl9SZWN1cnJlZCIpKSArCiAgbGFicyh0aXRsZSA9IHBhc3RlKCJVc2luZyBSIFNlc3Npb24sIFJ1bm5pbmcgdGltZSA9ICIsIFN5cy50aW1lKCkgLSBzdGFydF90aW1lKSkKYGBgCgojIyBTcGFyayBOb2RlOiBQbG90IERpc2Vhc2VGcmVlIHZzIFJlY2N1cmVkIGR1cmluZyBPU19NT05USFMgCmBgYHtyfQogY2xpbmljYWxEYXRhIDwtIGNnZHNyOjpnZXRDbGluaWNhbERhdGEoY2dkcywgImdibV90Y2dhX3B1Yl9hbGwiKQogc2MgPC0gc3BhcmtfY29ubmVjdChtYXN0ZXIgPSAibG9jYWwiLAogICAgICAgICAgICAgICAgICAgICB2ZXJzaW9uID0gIjIuNC4wIikKCiBjbGluaWNhbERhdGFfdGJsIDwtIGRwbHlyOjpjb3B5X3RvKHNjLCBjbGluaWNhbERhdGEsIG92ZXJ3cml0ZSA9IFRSVUUpCiAgc3RhcnRfdGltZSA8LSBTeXMudGltZSgpCiAgY2xpbmljYWxEYXRhX3RibCAlPiUKICBtdXRhdGUoT1NfU1RBVFVTID0gcmVnZXhwX3JlcGxhY2UoT1NfU1RBVFVTLCAiTElWSU5HIiwgIjAiKSkgJT4lCiAgbXV0YXRlKE9TX1NUQVRVUyA9IHJlZ2V4cF9yZXBsYWNlKE9TX1NUQVRVUywgIkRFQ0VBU0VEIiwgIjEiKSkgJT4lCiAgbXV0YXRlKERGU19TVEFUVVMgPSByZWdleHBfcmVwbGFjZShERlNfU1RBVFVTLCAiXiR8XiAkIiwgIkRpc2Vhc2VGcmVlIikpICU+JQogIG11dGF0ZShPU19TVEFUVVMgPSBhcy5udW1lcmljKE9TX1NUQVRVUykpICU+JQogICNtdXRhdGUoT1NfU1RBVFVTID0gcmVnZXhwX3JlcGxhY2UoYXMubnVtZXJpYyhPU19TVEFUVVMpLCAnTmFOJywgTkEpKSAlPiUKICAjbXV0YXRlKE9TX1NUQVRVUyA9IHJlZ2V4cF9yZXBsYWNlKE9TX1NUQVRVUywgTmFOLCBOQSkpICU+JQogICNuYS5yZXBsYWNlKCcnKSAlPiUgICMjIG5vdCBnb29kIGZvciBPU19TVEFUVVMgKDAsMSkKICAjZHBseXI6OmZpbHRlcighaXMubmEoT1NfTU9OVEhTKSkgCiAgYXJyYW5nZShpcy5uYShPU19NT05USFMpLCBPU19NT05USFMpICU+JSAgIyMgT1VGRkYgcHV0IE5hbiBhdCB0aGUgZW5kIG9mIHRoZSBjb2x1bW4KICBtdXRhdGUoRGlzZWFzZUZyZWUgPSBpZmVsc2UoREZTX1NUQVRVUyA9PSAiRGlzZWFzZUZyZWUiLCAxLCAwKSkgJT4lIAogIGFzLmRhdGEuZnJhbWUoKSAlPiUKICBtdXRhdGUoIG5fRGlzZWFzZUZyZWUgPSBjdW1zdW0oYXMubnVtZXJpYyhEaXNlYXNlRnJlZSA9PSAxICkpKSAlPiUKICBtdXRhdGUoIG5fUmVjdXJyZWQgPSBjdW1zdW0oYXMubnVtZXJpYyhEaXNlYXNlRnJlZSA9PSAwICkpKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBPU19NT05USFMsIHkgPSB2YWx1ZSwgY29sb3IgPSB2YXJpYWJsZSkpICsKICBnZW9tX3BvaW50KGFlcyh5ID0gbl9EaXNlYXNlRnJlZSwgY29sID0gIm5fRGlzZWFzZUZyZWUiKSkgKwogIGdlb21fcG9pbnQoYWVzKHkgPSBuX1JlY3VycmVkLCBjb2wgPSAibl9SZWN1cnJlZCIpKSAgKwogICBsYWJzKHRpdGxlID0gcGFzdGUoIlVzaW5nIFNwYXJrIE5vZGUsIFJ1bm5pbmcgdGltZSA9ICIsIFN5cy50aW1lKCkgLSBzdGFydF90aW1lKSkKYGBgCgoKIyBTdXJ2aXZhbCByZWdyZXNzaW9uIHVzaW5nICBgbWxfYWZ0X3N1cnZpdmFsX3JlZ3Jlc3Npb25gIHsudGFic2V0IC50YWJzZXQtZmFkZSAudGFic2V0LXBpbGxzfQoKIyMgT3ZhcmlhbiBEYXRhIGZyb20gYHN1cnZpdmFsYCBwYWNrYWdlCgpgYGB7cn0KbGlicmFyeShzdXJ2aXZhbCkKb3ZhcmlhbgpgYGAKCiMjIyBQcmVkaWN0IFN1cnZpdmFsIHJlZ3Jlc3Npb24gdXNpbmcgc3BhcmsKYGBge3J9CiMgTUFHSUMgLSAqKmZ1dGltZSoqOiBzdXJ2aXZhbCBvciBjZW5zb3JpbmcgdGltZQojIE1BR0lDIC0gKipmdXN0YXQqKjogY2Vuc29yaW5nIHN0YXR1cwojIE1BR0lDIC0gKiphZ2UqKjoJaW4geWVhcnMKIyBNQUdJQyAtICoqcmVzaWRfZHMqKjoJcmVzaWR1YWwgZGlzZWFzZSBwcmVzZW50ICgxPW5vLCAyPXllcykKIyBNQUdJQyAtICoqcngqKjoJdHJlYXRtZW50IGdyb3VwCiMgTUFHSUMgLSAqKmVjb2cucHMqKjoJRUNPRyBwZXJmb3JtYW5jZSBzdGF0dXMgKDEgaXMgYmV0dGVyLCBzZWUgcmVmZXJlbmNlKQoKc2MgPC0gc3BhcmtfY29ubmVjdChtYXN0ZXIgPSAibG9jYWwiLAogICAgICAgICAgICAgICAgICAgICB2ZXJzaW9uID0gIjIuNC4wIikKb3Zhcmlhbl90YmwgPC0gc2RmX2NvcHlfdG8oc2MsIG92YXJpYW4sIG5hbWUgPSAib3Zhcmlhbl90YmwiLCBvdmVyd3JpdGUgPSBUUlVFKQoKI3NwYXJrLnN1cnZyZWcob3Zhcmlhbl90YmwsIFN1cnYoZnV0aW1lLCBmdXN0YXQpIH4gZWNvZ19wcyArIHJ4KQoKcGFydGl0aW9ucyA8LSBvdmFyaWFuX3RibCAlPiUKICBzZGZfcGFydGl0aW9uKHRyYWluaW5nID0gMC43LCB0ZXN0ID0gMC4zLCBzZWVkID0gMTExMSkKCm92YXJpYW5fdHJhaW5pbmcgPC0gcGFydGl0aW9ucyR0cmFpbmluZwpvdmFyaWFuX3Rlc3QgPC0gcGFydGl0aW9ucyR0ZXN0CgpzdXJfcmVnIDwtIG92YXJpYW5fdHJhaW5pbmcgJT4lCiAgbWxfYWZ0X3N1cnZpdmFsX3JlZ3Jlc3Npb24oZnV0aW1lIH4gZWNvZ19wcyArIHJ4ICsgYWdlICsgcmVzaWRfZHMsIGNlbnNvcl9jb2wgPSAiZnVzdGF0IikKCnByZWQgPC0gbWxfcHJlZGljdChzdXJfcmVnLCBvdmFyaWFuX3Rlc3QpCnByZWQKYGBgCgojIyMgRXh0cmFjdCBwYXJhbWV0ZXJzCmBgYHtyfQppbnRlcmNlcHQgPC0gc3VyX3JlZyRjb2VmZmljaWVudHNbMV0KY29lZmZpY2llbnRzIDwtIHN1cl9yZWckY29lZmZpY2llbnRzW2MoMiwzKV0Kc3VyX3JlZyRjb2VmZmljaWVudHMKYGBgCgpgYGB7cn0KcGxvdFBhcmFtcyA8LSByb3VuZChvdmFyaWFuW2MoJ3Jlc2lkLmRzJywgJ3J4JywgJ2Vjb2cucHMnLCAnYWdlJyldKQpzY2FsZSA8LSBleHAoaW50ZXJjZXB0ICsgYXMubWF0cml4KHBsb3RQYXJhbXMpICogY29lZmZpY2llbnRzKQpjYmluZChwbG90UGFyYW1zLCBzY2FsZSkKYGBgCgpgYGB7cn0KdFNlcSA8LSBvdmFyaWFuJGZ1dGltZSAjIHNlcSgwLCA1RTMsIDUwKQpwcm9icyA8LSBkYXRhLmZyYW1lKHQgPSB0U2VxKQpmb3IgKGkgaW4gMToyXjQpIHsgCiAgcHJvYnNbLCBwYXN0ZSgiKHJlc2lkLmRzLCByeCwgZWNvZy5wcywgYWdlKSA9ICgiLCB0b1N0cmluZyhwbG90UGFyYW1zW2ksIF0pLCAiKSIsIHNlcCA9ICIiKV0gPC0gCiAgICBwd2VpYnVsbCh0U2VxLCBzaGFwZSA9IDEsIHNjYWxlID0gc2NhbGVbaV0sIGxvd2VyLnRhaWwgPSBGKQp9CnByb2JzCmBgYAoKIyMjIE1lbHQgdGhlIERhdGFGcmFtZQpgYGB7cn0KbGlicmFyeShyZXNoYXBlMikKIyBNQUdJQyAtICoqZnV0aW1lKio6IHN1cnZpdmFsIG9yIGNlbnNvcmluZyB0aW1lCiMgTUFHSUMgLSAqKmZ1c3RhdCoqOiBjZW5zb3Jpbmcgc3RhdHVzCiMgTUFHSUMgLSAqKmFnZSoqOglpbiB5ZWFycwojIE1BR0lDIC0gKipyZXNpZF9kcyoqOglyZXNpZHVhbCBkaXNlYXNlIHByZXNlbnQgKDE9bm8sIDI9eWVzKQojIE1BR0lDIC0gKipyeCoqOgl0cmVhdG1lbnQgZ3JvdXAKIyBNQUdJQyAtICoqZWNvZy5wcyoqOglFQ09HIHBlcmZvcm1hbmNlIHN0YXR1cyAoMSBpcyBiZXR0ZXIsIHNlZSByZWZlcmVuY2UpCgptZWx0ZWQgPC0gbWVsdChwcm9icywgaWQudmFycz0idCIsIHZhcmlhYmxlLm5hbWU9Imdyb3VwIiwgdmFsdWUubmFtZT0icHJvYiIpCm1lbHRlZApgYGAKCiMjIyBQbG90IHN1cnZpdmFsIHJlZ3Jlc3Npb24KYGBge3J9CmxpYnJhcnkoZ2dwbG90MikKCmdncGxvdChkYXRhPW1lbHRlZCwgYWVzKHg9dCwgeT1wcm9iLCBncm91cD1ncm91cCwgY29sb3I9Z3JvdXApKSArIAogIGdlb21fcG9pbnQoKSArCiAgI2dlb21fc21vb3RoKCkgKwogICNnZW9tX2ppdHRlcigpICsKICBsYWJzKHggPSAidGltZSIsIHkgPSAiU3Vydml2YWwgcHJvYmFiaWxpdHkiKQpgYGAKCiMjIENsaW5pY2FsIERhdGEgZnJvbSBgZ2JtX3RjZ2FfcHViX2FsbGAgU3R1ZHkgZnJvbSBjQmlvUG9ydGFsCgpgYGB7cn0KIyA+IG92YXJpYW5fdHJhaW5pbmcKIyAjIFNvdXJjZTogc3Bhcms8Pz4gWz8/IHggNl0KIyBmdXRpbWUgZnVzdGF0ICAgYWdlIHJlc2lkX2RzICAgIHJ4IGVjb2dfcHMKIyAqICA8ZGJsPiAgPGRibD4gPGRibD4gICAgPGRibD4gPGRibD4gICA8ZGJsPgojIDEgICAgMTU2ICAgICAgMSAgNjYuNSAgICAgICAgMiAgICAgMSAgICAgICAyCiMgMiAgICAzMjkgICAgICAxICA0My4xICAgICAgICAyICAgICAxICAgICAgIDEKIyAzICAgIDM1MyAgICAgIDEgIDYzLjIgICAgICAgIDEgICAgIDIgICAgICAgMgojIDQgICAgMzY1ICAgICAgMSAgNjQuNCAgICAgICAgMiAgICAgMiAgICAgICAxCiMgNSAgICAzNzcgICAgICAwICA1OC4zICAgICAgICAxICAgICAyICAgICAgIDEKIyA2ICAgIDQyMSAgICAgIDAgIDUzLjQgICAgICAgIDIgICAgIDIgICAgICAgMQojIDcgICAgNDQ4ICAgICAgMCAgNTYuNCAgICAgICAgMSAgICAgMSAgICAgICAyCiMgOCAgICA0NjQgICAgICAxICA1Ni45ICAgICAgICAyICAgICAyICAgICAgIDIKIyA5ICAgIDQ3NSAgICAgIDEgIDU5LjkgICAgICAgIDIgICAgIDIgICAgICAgMgojIDEwICAgNTYzICAgICAgMSAgNTUuMiAgICAgICAgMSAgICAgMiAgICAgICAyCiMgIyAuLi4gd2l0aCBtb3JlIHJvd3MKCmNsaW5pY2FsRGF0YSA8LSBjZ2Rzcjo6Z2V0Q2xpbmljYWxEYXRhKGNnZHMsICJnYm1fdGNnYV9wdWJfYWxsIikKCiNjbGluaWNhbERhdGEgPC0gcmVhZC5jc3YoIkNsaW5pY2FsX3RhYi5jc3YiKSAjLCBuYS5zdHJpbmdzPWMoIiIsIk5BIikKCmNsaW5pY2FsRGF0YSA8LSBjbGluaWNhbERhdGFbYygnT1NfTU9OVEhTJywgICdPU19TVEFUVVMnLCAnREZTX1NUQVRVUycsICAnVFJFQVRNRU5UX1NUQVRVUycgKV0Kc2MgPC0gc3BhcmtfY29ubmVjdChtYXN0ZXIgPSAibG9jYWwiLAogICAgICAgICAgICAgICAgICAgICB2ZXJzaW9uID0gIjIuNC4wIikKCiBjbGluaWNhbERhdGFfdGJsIDwtIGRwbHlyOjpjb3B5X3RvKHNjLCBjbGluaWNhbERhdGEsIG92ZXJ3cml0ZSA9IFRSVUUpCiAgc3RhcnRfdGltZSA8LSBTeXMudGltZSgpCiAgY2xpbmljYWxEYXRhX3RyYW5zX3RibCA8LSAKICBjbGluaWNhbERhdGFfdGJsICU+JQogIG11dGF0ZShPU19TVEFUVVMgPSByZWdleHBfcmVwbGFjZShPU19TVEFUVVMsICJMSVZJTkciLCAxKSkgJT4lCiAgbXV0YXRlKE9TX1NUQVRVUyA9IHJlZ2V4cF9yZXBsYWNlKE9TX1NUQVRVUywgIkRFQ0VBU0VEIiwgMCkpICU+JQogIG11dGF0ZShERlNfU1RBVFVTID0gcmVnZXhwX3JlcGxhY2UoREZTX1NUQVRVUywgIl4kfF4gJCIsICJEaXNlYXNlRnJlZSIpKSAlPiUKICBtdXRhdGUoREZTX1NUQVRVUyA9IHJlZ2V4cF9yZXBsYWNlKERGU19TVEFUVVMsICJEaXNlYXNlRnJlZSIsIDEpKSAlPiUKICBtdXRhdGUoREZTX1NUQVRVUyA9IHJlZ2V4cF9yZXBsYWNlKERGU19TVEFUVVMsICJSZWN1cnJlZCIsIDIpKSAlPiUKICBtdXRhdGUoeHIgPSBpZmVsc2UoVFJFQVRNRU5UX1NUQVRVUyA9PSAiVW50cmVhdGVkIiwgMSAsIDIpKSAlPiUKICBtdXRhdGUoeHIgPSBpZmVsc2UoVFJFQVRNRU5UX1NUQVRVUyA9PSAiVHJlYXRlZCIsIDIsIDEpKSAlPiUKICBtdXRhdGUoT1NfU1RBVFVTID0gYXMubnVtZXJpYyhPU19TVEFUVVMpKSAlPiUKICBtdXRhdGUoREZTX1NUQVRVUyA9IGFzLm51bWVyaWMoREZTX1NUQVRVUykpICU+JQogICNhcnJhbmdlKGlzLm5hKE9TX01PTlRIUyksIE9TX01PTlRIUykgJT4lICMjIE9VRkZGIHB1dCBOYW4gYXQgdGhlIGVuZCBvZiB0aGUgY29sdW1uCiAgbmEucmVwbGFjZSgxKQogIGNsaW5pY2FsRGF0YV90cmFucyA8LSBhcy5kYXRhLmZyYW1lKGNsaW5pY2FsRGF0YV90cmFuc190YmwpCiAgY2xpbmljYWxEYXRhX3RyYW5zX3RibApgYGAKCgpgYGB7cn0KcGFydGl0aW9uc19jbGluaWNhbERhdGEgPC0gY2xpbmljYWxEYXRhX3RyYW5zX3RibCAlPiUKICBzZGZfcGFydGl0aW9uKHRyYWluaW5nID0gMC43LCB0ZXN0ID0gMC4zLCBzZWVkID0gMTExMSkKCmNsaW5pY2FsRGF0YV90cmFpbmluZyA8LSBwYXJ0aXRpb25zX2NsaW5pY2FsRGF0YSR0cmFpbmluZwpjbGluaWNhbERhdGFfdGVzdCA8LSBwYXJ0aXRpb25zX2NsaW5pY2FsRGF0YSR0ZXN0CgpzdXJfcmVnX2NsaW5pY2FsRGF0YSA8LSBjbGluaWNhbERhdGFfdHJhaW5pbmcgJT4lCiAgbWxfYWZ0X3N1cnZpdmFsX3JlZ3Jlc3Npb24oT1NfTU9OVEhTIH4gREZTX1NUQVRVUyArIHhyLCBjZW5zb3JfY29sID0gIk9TX1NUQVRVUyIpCgpwcmVkX2NsaW5pY2FsRGF0YSA8LSBtbF9wcmVkaWN0KHN1cl9yZWdfY2xpbmljYWxEYXRhLCBjbGluaWNhbERhdGFfdGVzdCkKcHJlZF9jbGluaWNhbERhdGEKYGBgCgojIyMgRXh0cmFjdCBwYXJhbWV0ZXJzIGZvciBDbGluaWNhbCBEYXRhCmBgYHtyfQppbnRlcmNlcHRfY2xpbmljYWxEYXRhIDwtIHN1cl9yZWdfY2xpbmljYWxEYXRhJGNvZWZmaWNpZW50c1sxXQpjb2VmZmljaWVudHNfY2xpbmljYWxEYXRhIDwtIHN1cl9yZWckY29lZmZpY2llbnRzW2MoMiwzKV0Kc3VyX3JlZ19jbGluaWNhbERhdGEkY29lZmZpY2llbnRzCmBgYAoKYGBge3J9CnBsb3RQYXJhbXNfY2xpbmljYWxEYXRhIDwtIGNsaW5pY2FsRGF0YV90cmFuc1tjKCdERlNfU1RBVFVTJywgJ3hyJyldCnNjYWxlX2NsaW5pY2FsRGF0YSA8LSBleHAoaW50ZXJjZXB0X2NsaW5pY2FsRGF0YSArIGFzLm1hdHJpeChwbG90UGFyYW1zX2NsaW5pY2FsRGF0YSkgKiBjb2VmZmljaWVudHNfY2xpbmljYWxEYXRhKQpjYmluZChwbG90UGFyYW1zX2NsaW5pY2FsRGF0YSwgc2NhbGVfY2xpbmljYWxEYXRhKQpgYGAKCgoKYGBge3J9CnRTZXFfY2xpbmljYWxEYXRhIDwtIGNsaW5pY2FsRGF0YV90cmFucyRPU19NT05USFMgIyBzZXEoMCwgNUUzLCA1MCkKcHJvYnNfY2xpbmljYWxEYXRhIDwtIGRhdGEuZnJhbWUodCA9IHRTZXFfY2xpbmljYWxEYXRhKQpmb3IgKGkgaW4gMTo1KSB7IAogIHByb2JzX2NsaW5pY2FsRGF0YVssIHBhc3RlKCIoREZTX1NUQVRVUywgeHIpID0gKCIsIHRvU3RyaW5nKHBsb3RQYXJhbXNfY2xpbmljYWxEYXRhW2ksIF0pLCAiKSIsIHNlcCA9ICIiKV0gPC0gCiAgICBwd2VpYnVsbCh0U2VxX2NsaW5pY2FsRGF0YSwgc2hhcGUgPSAxLCBzY2FsZSA9IHNjYWxlX2NsaW5pY2FsRGF0YVtpXSwgbG93ZXIudGFpbCA9IEYpCn0KcHJvYnNfY2xpbmljYWxEYXRhCmBgYAoKCmBgYHtyfQpsaWJyYXJ5KHJlc2hhcGUyKQptZWx0ZWRfY2xpbmljYWxEYXRhIDwtIG1lbHQocHJvYnNfY2xpbmljYWxEYXRhLCBpZC52YXJzPSJ0IiwgdmFyaWFibGUubmFtZT0iZ3JvdXAiLCB2YWx1ZS5uYW1lPSJwcm9iIikKbWVsdGVkX2NsaW5pY2FsRGF0YQpgYGAKCgpgYGB7cn0KbGlicmFyeShnZ3Bsb3QyKQpnZ3Bsb3QoZGF0YT1tZWx0ZWRfY2xpbmljYWxEYXRhLCBhZXMoeD10LCB5PXByb2IsIGdyb3VwPWdyb3VwLCBjb2xvcj1ncm91cCkpICsgCiAgZ2VvbV9wb2ludCgpICsKICAjZ2VvbV9zbW9vdGgoKSArCiAgI2dlb21faml0dGVyKCkgKwogIGxhYnMoeCA9ICJ0aW1lIiwgeSA9ICJTdXJ2aXZhbCBwcm9iYWJpbGl0eSIpCmBgYAoK